﻿using System;
using System.Collections.Generic;
using OpenTK;

namespace Painter3D.RenderSys
{
    /// <summary>
    /// 材质系统，解决着色器的赋值功能
    /// </summary>
    public class Material
    {
        readonly public Shader shader;
        readonly private Object[] data;

        /// <summary>
        /// 设置uniform初始值的委托列表
        /// </summary>
        static private Dictionary<Type, Delegate> dic = new Dictionary<Type, Delegate>()
            {
                { typeof(Matrix4),new Func<Matrix4>(()=>{return Matrix4.Identity; })},
                { typeof(Vector4),new Func<Vector4>(()=>{return Vector4.Zero; })},
                { typeof(Vector3),new Func<Vector3>(()=>{return Vector3.Zero; })},
                { typeof(Vector2),new Func<Vector2>(()=>{return Vector2.Zero; })},
                { typeof(Texture2D),new Func<Texture2D>(()=>{return null; })},
                { typeof(TextureBox),new Func<Texture2D>(()=>{return null; })},
            };
        /// <summary>
        /// 以默认初值完成材质创建
        /// </summary>
        /// <param name="shader">着色器源</param>
        public Material(Shader shader)
        {
            this.shader = shader ?? throw new ArgumentNullException(nameof(shader));
            this.data = new Object[shader.info.uniformInfos.Length];
            //着色器的初值
            for (int i = 0; i < shader.info.uniformInfos.Length; i++)
            {
                data[i] = dic[shader.info.uniformInfos[i].type].DynamicInvoke();
                if (shader.info.uniformInfos[i].name == "mainColor")
                    data[i] = new Vector4(0, 0, 0, 1);
            }
        }
        /// <summary>
        /// 带有初始值的材质创建
        /// </summary>
        /// <param name="shader">着色器源</param>
        /// <param name="values">参数列表 name-value</param>
        public Material(Shader shader, object[] values) : this(shader)
        {
            if (values.Length % 2 != 0)
                throw new Exception("着色器参数初始化未成对赋值");
            //特值
            for (int i = 0; i < values.Length / 2; i++)
            {
                string name = values[i * 2] as string;
                for (int k = 0; k < shader.info.uniformInfos.Length; k++)
                {
                    ShaderInfo.UniformInfo uniformInfo = shader.info.uniformInfos[k];
                    if (uniformInfo.name == name)
                    {
                        //最好添加一个类型转换的过程
                        data[k] = values[i * 2 + 1];
                    }
                }
            }
        }

        /// <summary>
        /// 设置材质的取值
        /// </summary>
        /// <typeparam name="T">参数类型</typeparam>
        /// <param name="name">参数名</param>
        /// <param name="value">值</param>
        /// <returns>是否赋值成功</returns>
        public bool SetValue<T>(string name, T value)
        {
            for (int i = 0; i < shader.info.uniformInfos.Length; i++)
            {
                ShaderInfo.UniformInfo uniformInfo = shader.info.uniformInfos[i];
                if (uniformInfo.name == name)
                {
                    if (uniformInfo.type != typeof(T))
                    {
                        return false;
                    }
                    data[i] = value;
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// 获取材质的值
        /// </summary>
        /// <typeparam name="T">参数类型</typeparam>
        /// <param name="name">参数名</param>
        /// <param name="value">值</param>
        /// <returns>是否取值成功</returns>
        public bool GetValue<T>(string name, out T value)
        {
            for (int i = 0; i < shader.info.uniformInfos.Length; i++)
            {
                ShaderInfo.UniformInfo uniformInfo = shader.info.uniformInfos[i];
                if (uniformInfo.name == name)
                {
                    if (uniformInfo.type != typeof(T))
                    {
                        value = default(T);
                        return false;
                    }
                    value = (T)data[i];
                    return true;
                }
            }
            value = default(T);
            return false;
        }
        /// <summary>
        /// 向显卡更新材质信息
        /// </summary>
        public void UploadData()
        {
            shader.Use();
            for (int i = 0; i < shader.info.uniformInfos.Length; i++)
            {
                if (shader.info.uniformInfos[i].type == typeof(Texture2D))
                {
                    Texture2D texture2D = data[i] as Texture2D;
                    texture2D.Use(shader.info.uniformInfos[i].index);
                }
                else if (shader.info.uniformInfos[i].type == typeof(TextureBox))
                {
                    TextureBox textureBox = data[i] as TextureBox;
                    textureBox.Use(shader.info.uniformInfos[i].index);
                }
                else
                {
                    shader.UploadUniform(shader.info.uniformInfos[i].name, data[i], shader.info.uniformInfos[i].type);
                }
            }
        }

        /// <summary>
        /// <para>为材质对象参数赋值</para> 
        /// <para>语法糖: new Material().WithValue()..WithValue()</para> 
        /// </summary>
        /// <typeparam name="T">参数数据类型</typeparam>
        /// <param name="name">参数名称</param>
        /// <param name="value">取值</param>
        /// <returns></returns>
        public Material WithValue<T>(string name, T value)
        {
            SetValue(name, value);
            return this;
        }
    }
}
